<?php
defined('IN_XLP') || exit('access denied');

class weixinPay {
	//微信参数
	private static $_wx = null;
	//微信URL
	private $_url = array();
	//请求类
	private $_request   = null;
	//回调地址
	private $_callback  = null;
	//支付notify url
	private $_notifyURL = null;
	
	function __construct( $setting=array() ){

		error_reporting(0);
		session_start();

		$params = array();

		/**
		 * 注意以下微信的基本5个参数
		 * 腾讯一般是分两封邮件发给你的
		 * 第1封是“成功通过公众平台商户审核” 里面包含 AppID 、AppSecret 和 PaySignKey
		 * 第2封是“财付通商户申请资料已经通过” 里面包含 PartnerID 和 PartnerKey
		 *
		 * 另外注意坑爹的微信支付文档里面的 appkey，一般指的就是 PaySignKey
		 */

		$params['appid']        = $setting['wx_AppId'];
		$params['appsecret']    = $setting['wx_AppSecret'];
		$params['partner_id']   = $setting['wx_PartnerId'];
		$params['partner_key']  = $setting['wx_PartnerKey'];
		$params['pay_sign_key'] = $setting['wx_PaySignKey'];
		
		self::$_wx = $params;
		$this->_request = new Request();
	}

	/**
	 * 检查token是否过期
	 * @param  int  $add_time
	 * @param  int  $expires_in
	 * @return boolean
	 */
	private function _isExpire($add_time,$expires_in){
		return $add_time + $expires_in < time();
	}

	/**
	 * 生成随机数 默认32位
	 * @return String
	 */
	private function _random($length = 32){
		if($length > 32 || $length <= 0) $length = 32;
		$random = md5(uniqid(mt_rand(1000,9999),true));
		return substr($random, 0, $length);
	}


	/**
	 * 处理微信服务器返回的信息
	 * @param  Array $result 待处理信息
	 * @param  String $comment 错误说明
	 * @return Array 微信各项参数
	 */
	private function _errorHandler($result,$comment=''){
		//HTTP返回 200 OK
		if($result['code']=='200'){
			$content = json_decode($result['content'],true);
			//微信返回错误码
			if(isset($content['errcode']) AND $content['errcode'] !== 0){
				throw new WechatException($content['errcode'].':'.$content['errmsg'].' -- '.$comment);
			}
			//返回正确结果
			return $content;
		}else{
			//HTTP 错误
			throw new WechatException('HTTP response code: '.$result['code'].' -- '.$comment);
		}
	}

	public function getWechatParams($key=null){
		return $key ? self::$_wx[$key] : self::$_wx;
	}


	/**
	 * 设置Oauth2.0回调地址
	 * @param String $url
	 */
	public function setCallback($url){
		$this->_callback = urlencode($url);
	}

	/**
	 * 获取服务号基础支持的access_token
	 * @return Array base_token相关参数
	 */
	public function fetchBaseAccessToken(){
		//是否设置了有效token
		if(isset(self::$_wx['base_token'])){
			if( !$this->_isExpire(self::$_wx['base_token_addtime'],self::$_wx['base_token_expires_in']) ){
				return self::$_wx['base_token'];
			}
		}

		//全局access_token URL
		$this->_url['base_token']
		= 'https://api.weixin.qq.com/cgi-bin/token?'
		 .'grant_type=client_credential'
		 .'&appid='.self::$_wx['appid']
		 .'&secret='.self::$_wx['appsecret'];

		//请求base_token
		$result  = $this->_request->get($this->_url['base_token']);
		$content = $this->_errorHandler($result,'Cannot get base access_token.');
		
		//类属性 更新token
		self::$_wx['base_token']            = $content['access_token'];
		self::$_wx['base_token_addtime']    = time();
		self::$_wx['base_token_expires_in'] = $content['expires_in'];

		return self::$_wx;
	}


	/**
	 * Oauth2.0协议 请求code的URL
	 * @param  String $scope (#1 snsapi_base 不弹页面只获得OpenId #2 snsapi_userinfo 弹出授权页面获取更多用户信息)
	 * @return
	 */
	public function oauthCodeURL($scope='snsapi_base'){

		if( !isset($this->_callback) ){
			throw new WechatException('Redirect uri not set');
		}

		//CSRF Protection State
		$state = $_SESSION['redirect_state'] = $this->_random();

		if( $scope!='snsapi_base' && $scope!='snsapi_userinfo' ){
			throw new WechatException('scope type is not correct');
		}

		$this->_url['oauth_code']
		= 'https://open.weixin.qq.com/connect/oauth2/authorize?'
		 .'appid='.self::$_wx['appid']
		 .'&redirect_uri='.$this->_callback
		 .'&response_type=code'
		 .'&scope='.$scope
		 .'&state='.$state
		 .'#wechat_redirect';

		//注意只能用header
		header('Location:'.$this->_url['oauth_code']);

		return;
	}

	/**
	 * 微信服务器回调返回 Oauth2.0 token
	 * @return Array  
	 */
	public function getOauthToken(){

		//用户拒绝授权
		if( $_GET['code'] == 'authdeny' ){
			throw new WechatException('User refuses to authorize');
		}

		//CSRF Protection
		if($_GET['state'] == $_SESSION['redirect_state']){

			$this->_url['oauth_token']
			= 'https://api.weixin.qq.com/sns/oauth2/access_token?'
			 .'appid='.self::$_wx['appid']
			 .'&secret='.self::$_wx['appsecret']
			 .'&code='.$_GET['code']
			 .'&grant_type=authorization_code';

			//获取token
			$result  = $this->_request->get($this->_url['oauth_token']);

			$content = $this->_errorHandler($result,'Can not get OAuth2.0 token.');
			
			$content['addtime'] = time();
			return $content;

		}else{
			throw new WechatException('You may be a victim of CSRF');
		}
	}

	/**
	 * 使用refresh_token刷新token
	 * @param  String $refresh_token
	 * @return Array 新的token相关参数
	 */
	private function _refreshToken($refresh_token){

		$this->_url['get_refresh_token']
		= 'https://api.weixin.qq.com/sns/oauth2/refresh_token?'
		 .'appid='.self::$_wx['appid']
		 .'&grant_type=refresh_token'
		 .'&refresh_token='.$refresh_token;

		$result  = $this->_request->get($this->_url['get_refresh_token']);
		$content = $this->_errorHandler($result,'Fail to refresh token.');

		$content['addtime'] = time();

		return $content;
	}

	/**
	 * 获取用户详细信息
	 * @param  String $access_token
	 * @param  String $openid
	 * @return Array 用户信息
	 */
	public function getWebUserInfo($access_token,$openid){

		$this->_url['get_web_userinfo']
		= 'https://api.weixin.qq.com/sns/userinfo?'
		 .'access_token='.$access_token
		 .'&openid='.$openid;
		
		$result  = $this->_request->get($this->_url['get_web_userinfo']);
		$content = $this->_errorHandler($result,'Fail to get web user info');

		return $content;
	}

	/**
	 * 生成获取用户收货地址所必须的参数集
	 * @param  String $access_token Oauth2.0的token
	 * @return String json格式的参数集
	 */
	public function genAddrParams($access_token){

		$sign   = new Sign();
		$time   = time();
		//IOS系统随机数不能太长 为了兼容取12位
		$random = $this->_random(12);

		$addr_params = array(
				'appId'       => self::$_wx['appid'],
				'url'         => "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'],
				'timeStamp'   => "$time",
				'nonceStr'    => $random,
				'accessToken' => $access_token
			);

		$sign->setParams($addr_params);
		//获得地址签名
		$addr_sign = $sign->genAddrSign();

		$params = array(
				'appId'     => self::$_wx['appid'],
				'scope'     => 'jsapi_address',
				'signType'  => 'sha1',
				'addrSign'  => $addr_sign,
				'timeStamp' => "$time",
				'nonceStr'  => $random,
			);

		return json_encode($params);
	}

	/**
	 * 设置微信付款成功通知地址
	 * @param String $url
	 */
	public function setNotifyURL($url){
		$this->_notifyURL = $url;
	}


	/**
	 * 生成微信支付参数集
	 * @param  Array $order_params
	 * @return String json格式的参数集
	 */
	public function genPayParams($order_params){
		
		//package 所需字段说明见文件末 共15个参数

		if( !is_array($order_params) )
			throw new WechatException('Order package parameters must be an array');

		$sign   = new Sign();
		$random = $this->_random();
		$time   = time();

		$arr = array(
				'bank_type'        => 'WX',
				'partner'          => self::$_wx['partner_id'],
				'fee_type'         => '1',
				'notify_url'       => $this->_notifyURL,
				'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
				'input_charset'    => 'UTF-8'
			);

		$arr = array_merge($arr,$order_params);
		$sign->setParams($arr);
		//生成订单详情扩展字符串
		$order_package = $sign->genOrderPackage(self::$_wx['partner_key']);

		$arr = array(
				'appId'     => self::$_wx['appid'],
				'timeStamp' => $time,
				'nonceStr'  => $random,
				'package'   => $order_package,
				'appKey'    => self::$_wx['pay_sign_key']
			);

		$sign->setParams($arr);
		//生成支付签名
		$pay_sign = $sign->genPaySign();
		//最终参数集 所有值均为字符串
		$arr = array(
				'appId'     => self::$_wx['appid'],
				'timeStamp' => "$time",	//字符串
				'nonceStr'  => $random,
				'package'   => $order_package,
				'signType'  => "SHA1",
				'paySign'   => $pay_sign
			);

		return $arr;
	}

	/**
	 * 发货通知接口
	 * @param  string $access_token 基础支持access_token
	 * @param  string $openid       买家openid
	 * @param  string $out_sn       订单号
	 * @param  string $deliver_msg  发货状态信息 UTF-8
	 */
	public function delivery($access_token, $openid, $out_sn, $transid, $deliver_msg='ok') {
		//发货通知URL
		$this->_url['delivery'] = 'https://api.weixin.qq.com/pay/delivernotify?'
								 .'access_token='.$access_token;


		$time = time();
		// 1表示发货成功 0表示失败
		$deliver_status = $deliver_msg==='ok' ? "1" : "0";

		//生成签名
		$sign = new Sign();
		$sign->setParams(array(
				"appid"             => self::$_wx['appid'],
				"appkey"            => self::$_wx['pay_sign_key'],
				"openid"            => $openid,
				"transid"           => $transid,
				"out_trade_no"      => $out_sn,
				"deliver_timestamp" => "$time",
				"deliver_status"    => $deliver_status,
				"deliver_msg"       => $deliver_msg,
			));
		$sign = $sign->genAddrSign();

		//POST发货数据
		$post_data = array(
				"appid"             => self::$_wx['appid'],
				"openid"            => $openid,
				"transid"           => $transid,
				"out_trade_no"      => $out_sn,
				"deliver_timestamp" => "$time",
				"deliver_status"    => $deliver_status,
				"deliver_msg"       => $deliver_msg,
				"app_signature"     => $sign,
				"sign_method"       => "sha1",
			);

		$post_data = json_encode($post_data);

		$result = $this->_request->post($this->_url['delivery'], $post_data);
		return $result;
	}



	/**
	 * 生成原生支付URL
	 * @param  String $out_sn 外部交易号
	 * @return String         原生支付URL
	 */
	public function genNativePayURL($out_sn){

		if(empty($out_sn)) 
			return 'function genNativePayURL: missing or invalid parameter';

		$time   = time();
		$random = $this->_random();
		$native_pay_url = 'weixin://wxpay/bizpayurl?'
						 .'appid='.self::$_wx['appid']
						 .'&productid='.$out_sn
						 .'&timestamp='.$time
						 .'&noncestr='.$random;

		$params = array(
				"appid"     => self::$_wx['appid'],
				"timestamp" => $time,
				"noncestr"  => $random,
				"productid" => $out_sn,
				"appkey"    => self::$_wx['pay_sign_key'],
			);

		$sign = new Sign();
		$sign->setParams($params);
		$sign = $sign->genPaySign();

		$native_pay_url .= '&sign='.$sign;

		return $native_pay_url;
	}

	/**
	 * 原生支付时返回给微信服务器订单信息
	 * @param  Array  $order_params 订单所必要的参数 (body,out_trade_no,total_fee)
	 * @param  Integer $retcode     订单状态代码 0表示没问题
	 * @param  String  $reterrmsg   订单信息 可用来填写UTF-8编码提示 如“商品已下架”等……
	 * @return String               XML格式的订单信息
	 */
	public function genNativePayXML($order_params, $retcode=0, $reterrmsg='OK'){

		//package 所需字段说明见微信支付文档
		
		if( !is_array($order_params) )
			throw new WechatException('Order package parameters must be an array');

		$sign   = new Sign();
		$random = $this->_random();
		$time   = time();

		$arr = array(
				'bank_type'        => 'WX',
				'partner'          => self::$_wx['partner_id'],
				'fee_type'         => '1',
				'notify_url'       => $this->_notifyURL,
				'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
				'input_charset'    => 'UTF-8'
			);

		$arr = array_merge($arr,$order_params);
		$sign->setParams($arr);
		//生成订单详情扩展字符串
		$order_package = $sign->genOrderPackage(self::$_wx['partner_key']);

		//生成支付签名AppSignature
		$arr = array(
				'appId'     => self::$_wx['appid'],
				'appKey'    => self::$_wx['pay_sign_key'],
				'package'   => $order_package,
				'timeStamp' => $time,
				'nonceStr'  => $random,
				'retcode'   => $retcode,
				'reterrmsg' => $reterrmsg,
			);

		$sign->setParams($arr);
		$app_signature = $sign->genPaySign();

		$appid = self::$_wx['appid'];

		$native_pay_xml = <<<WEIXIN_XML
			<xml>
				<AppId><![CDATA[{$appid}]]></AppId>
				<NonceStr><![CDATA[{$random}]]></NonceStr>
				<TimeStamp>{$time}</TimeStamp>
				<Package><![CDATA[{$order_package}]]></Package>
				<RetCode>{$retcode}</RetCode>
				<RetErrMsg><![CDATA[{$reterrmsg}]]></RetErrMsg>
				<SignMethod><![CDATA[sha1]]></SignMethod>
				<AppSignature><![CDATA[{$app_signature}]]></AppSignature>
			</xml>
WEIXIN_XML;
		
		return $native_pay_xml;
	}


	/**
	 * 获取关注者的详细信息
	 * @param  String $openid
	 * @return Array 昵称 性别 所在地 头像...等等参数
	 */
	public function getUserInfo($openid){
		$base_token = $this->fetchBaseAccessToken();
		$base_token = $base_token['base_token'];

		$this->_url['get_userinfo']
		= 'https://api.weixin.qq.com/cgi-bin/user/info?'
		 .'access_token='.$base_token
		 .'&openid='.$openid
		 .'&lang=zh_CN';

		$result  = $this->_request->get($this->_url['get_userinfo']);
		$content = $this->_errorHandler($result,'Fail to get user info');

		return $content;
	}


}

/*
 * 微信签名方法类
 */

class Sign{

	private $_partnerKey = null;
	private $_params    = array();

	function __construct(){	
	}

	public function setKey($key){
		$this->_partnerKey = $key;
	}

	public function setParams($arr){
		//清空原参数
		$this->_params = array();

		if(is_array($arr)){
			foreach ($arr as $k => $v){
				$this->_params[$k] = $v;
			}
		}else{
			exit('Parameters must be an array');
		}
	}

	public function genAddrSign(){
		$str = '';
		ksort($this->_params);
		foreach ($this->_params as $k => $v){
			$str .= strtolower($k).'='.$v.'&';
		}
		$str = rtrim($str,'&');

		return sha1($str);
	}

	public function genOrderPackage($partnerKey){
		$str1 = '';
		$str2 = '';
		ksort($this->_params);
		foreach ($this->_params as $k => $v){
			$str1 .= $k.'='.$v.'&';
			$str2 .= $k.'='.urlencode($v).'&';
		}
		$str1 .= 'key='.$partnerKey;

		$signValue = strtoupper(md5($str1));
		//最终字符串
		$package   = $str2.'sign='.$signValue;

		return $package;
	}

	public function genPaySign(){
		return $this->genAddrSign();
	}


	public function checkNotifySign($partnerKey){
		$str  = '';
		//获取GET参数取出sign值
		$arr  = $_GET;
		$sign = $arr['sign'];
		unset($arr['sign']);

		ksort($arr);
		foreach ($arr as $k => $v){
			$str .= $k.'='.$v.'&';
		}

		$str .= 'key='.$partnerKey;
		return $sign === strtoupper(md5($str));
	}

	public function checkNotifyPost($appid,$appkey){
		//获取notify的post部分并解析
		$xml  = file_get_contents('php://input');
		$data = simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOCDATA);

		$params = array(
				'appid'       => $appid,
				'appkey'      => $appkey,
				'timestamp'   => $data->TimeStamp,
				'noncestr'    => $data->NonceStr,
				'openid'      => $data->OpenId,
				'issubscribe' => $data->IsSubscribe
			);

		$this->setParams($params);
		//和地址签名方法一样
		$sign = $this->genAddrSign();

		return strcmp($sign,$data->AppSignature) === 0;
	}


	public function checkNativeCallback($appkey){
		//获取推送的post部分并解析
		$xml  = file_get_contents('php://input');
		$data = simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOCDATA);

		$params = array(
				'appid'       => $data->AppId,
				'appkey'      => $appkey,
				'productid'   => $data->ProductId,
				'timestamp'   => $data->TimeStamp,
				'noncestr'    => $data->NonceStr,
				'openid'      => $data->OpenId,
				'issubscribe' => $data->IsSubscribe,
			);

		$this->setParams($params);
		//和地址签名方法一样
		$sign = $this->genAddrSign();

		if( strcmp($sign,$data->AppSignature) === 0 ) {
			//返回外部订单号 方便下一步生成订单Package
			return (string)$data->ProductId;
		} else {
			return false;
		}
	}


}


/*
 * 微信异常类
 */

class WechatException extends Exception{
	private $error_msg = null;
	function __construct($msg){
		$this->setMsg($msg);
	}
	public function setMsg($msg){
		$this->error_msg = $msg;
	}
	public function getMsg(){
		return $this->error_msg;
	}
}


/**
 * HTTP请求类
 */

class Request{

	function get($url) {
		return $this->ihttp_request($url);
	}

	function post($url, $data) {
		$headers = array('Content-Type' => 'application/x-www-form-urlencoded');
		return $this->ihttp_request($url, $data, $headers);
	}

	function ihttp_request($url, $post = '', $extra = array(), $timeout = 60) {
		$urlset = parse_url($url);
		if(empty($urlset['path'])) {
			$urlset['path'] = '/';
		}
		if(!empty($urlset['query'])) {
			$urlset['query'] = "?{$urlset['query']}";
		}
		if(empty($urlset['port'])) {
			$urlset['port'] = $urlset['scheme'] == 'https' ? '443' : '80';
		}
		if (strpos($url, 'https://')!==false && !extension_loaded('openssl')) {
			if (!extension_loaded("openssl")) {
				exit('openssl not enabled');
			}
		}
		if(function_exists('curl_init') && function_exists('curl_exec')) {
			$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, $urlset['scheme']. '://' .$urlset['host'].($urlset['port'] == '80' ? '' : ':'.$urlset['port']).$urlset['path'].$urlset['query']);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt($ch, CURLOPT_HEADER, 1);
			if($post) {
				curl_setopt($ch, CURLOPT_POST, 1);
				if (is_array($post)) {
					$post = http_build_query($post);
				}
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
			}
			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
			curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
			curl_setopt($ch, CURLOPT_SSLVERSION, 3);
			curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:9.0.1) Gecko/20100101 Firefox/9.0.1');
			if (!empty($extra) && is_array($extra)) {
				$headers = array();
				foreach ($extra as $opt => $value) {
					if (strpos($opt, 'CURLOPT_')!==false) {
						curl_setopt($ch, constant($opt), $value);
					} elseif (is_numeric($opt)) {
						curl_setopt($ch, $opt, $value);
					} else {
						$headers[] = "{$opt}: {$value}";
					}
				}
				if(!empty($headers)) {
					curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
				}
			}
			$data   = curl_exec($ch);
			$status = curl_getinfo($ch);
			$errno  = curl_errno($ch);
			$error  = curl_error($ch);
			curl_close($ch);
			if($errno || empty($data)) {
				return $status;
			} else {
				return $this->ihttp_response_parse($data);
			}
		}
		$method = empty($post) ? 'GET' : 'POST';
		$fdata = "{$method} {$urlset['path']}{$urlset['query']} HTTP/1.1\r\n";
		$fdata .= "Host: {$urlset['host']}\r\n";
		if(function_exists('gzdecode')) {
			$fdata .= "Accept-Encoding: gzip, deflate\r\n";
		}
		$fdata .= "Connection: close\r\n";
		if (!empty($extra) && is_array($extra)) {
			foreach ($extra as $opt => $value) {
				if (strpos($opt, 'CURLOPT_')===false) {
					$fdata .= "{$opt}: {$value}\r\n";
				}
			}
		}
		$body = '';
		if ($post) {
			if (is_array($post)) {
				$body = http_build_query($post);
			} else {
				$body = urlencode($post);
			}
			$fdata .= 'Content-Length: ' . strlen($body) . "\r\n\r\n{$body}";
		} else {
			$fdata .= "\r\n";
		}
		if($urlset['scheme'] == 'https') {
			$fp = fsockopen('ssl://' . $urlset['host'], $urlset['port'], $errno, $error);
		} else {
			$fp = fsockopen($urlset['host'], $urlset['port'], $errno, $error);
		}
		stream_set_blocking($fp, true);
		stream_set_timeout($fp, $timeout);
		if (!$fp) {
			return 'fsockopen fail';
		} else {
			fwrite($fp, $fdata);
			$content = '';
			while (!feof($fp))
				$content .= fgets($fp, 512);
			fclose($fp);
			return $this->ihttp_response_parse($content, true);
		}
	}

	function ihttp_response_parse($data, $chunked = false) {
		$rlt = array();
		$pos = strpos($data, "\r\n\r\n");
		$split1[0] = substr($data, 0, $pos);
		$split1[1] = substr($data, $pos + 4, strlen($data));
		
		$split2 = explode("\r\n", $split1[0], 2);
		$matches = array();
		preg_match('/^(\S+) (\S+) ([\w\s]+)$/', $split2[0], $matches);
		$rlt['code'] = $matches[2];
		$rlt['status'] = $matches[3];
		$rlt['responseline'] = $split2[0];
		$header = explode("\r\n", $split2[1]);
		$isgzip = false;
		$ischunk = false;
		foreach ($header as $v) {
			$row = explode(':', $v);
			$key = trim($row[0]);
			$value = trim($row[1]);
			if (is_array($rlt['headers'][$key])) {
				$rlt['headers'][$key][] = $value;
			} elseif (!empty($rlt['headers'][$key])) {
				$temp = $rlt['headers'][$key];
				unset($rlt['headers'][$key]);
				$rlt['headers'][$key][] = $temp;
				$rlt['headers'][$key][] = $value;
			} else {
				$rlt['headers'][$key] = $value;
			}
			if(!$isgzip && strtolower($key) == 'content-encoding' && strtolower($value) == 'gzip') {
				$isgzip = true;
			}
			if(!$ischunk && strtolower($key) == 'transfer-encoding' && strtolower($value) == 'chunked') {
				$ischunk = true;
			}
		}
		if($chunked && $ischunk) {
			$rlt['content'] = $this->ihttp_response_parse_unchunk($split1[1]);
		} else {
			$rlt['content'] = $split1[1];
		}
		if($isgzip && function_exists('gzdecode')) {
			$rlt['content'] = gzdecode($rlt['content']);
		}

		$rlt['meta'] = $data;
		if($rlt['code'] == '100') {
			return $this->ihttp_response_parse($rlt['content']);
		}
		return $rlt;
	}

	function ihttp_response_parse_unchunk($str = null) {
		if(!is_string($str) or strlen($str) < 1) {
			return false; 
		}
		$eol = "\r\n";
		$add = strlen($eol);
		$tmp = $str;
		$str = '';
		do {
			$tmp = ltrim($tmp);
			$pos = strpos($tmp, $eol);
			if($pos === false) {
				return false;
			}
			$len = hexdec(substr($tmp, 0, $pos));
			if(!is_numeric($len) or $len < 0) {
				return false;
			}
			$str .= substr($tmp, ($pos + $add), $len);
			$tmp  = substr($tmp, ($len + $pos + $add));
			$check = trim($tmp);
		} while(!empty($check));
		unset($tmp);
		return $str;
	}

}